// @Description: cron task计划任务相关的处理
package crontask

import (
    "sync"
    "github.com/jakecoffman/cron"
    "github.com/astaxie/beego/logs"
    "fmt"
    "strings"
    "time"
    "errors"
    "cloud_worm/util/uweb/ucron"
)

const (
    CronWeaponTask = 1
    CronOtherTask = 2
)

/**
 @Description：描述信息模式的检查，包括：
        "@yearly", "@annually"，"@monthly"，"@weekly"，"@daily", "@midnight"，"@hourly"，"@every "
 @Param:
 @Return：
 */
func ParseDescriptor(spec string) error {
    switch spec {
    case "@yearly", "@annually":
        return nil
    case "@monthly":
        return nil
    case "@weekly":
        return nil
    case "@daily", "@midnight":
        return nil
    case "@hourly":
        return nil
    }

    const every = "@every "
    if strings.HasPrefix(spec, every) {
        _, err := time.ParseDuration(spec[len(every):])
        if err != nil {
            return errors.New(fmt.Sprintf("Failed to parse duration %s: %s", spec, err.Error()))
        }
        return nil
    }

    return errors.New(fmt.Sprintf("Unrecognized descriptor: %s", spec))
}

/**
 @Description：计划任务输入的spec进行检查，接收的模式有：
        1. Full crontab specs, e.g. "* * * * * ?"
        2. Descriptors, e.g. "@midnight", "@every 1h30m"
 @Param:
 @Return：
 */
func CheckSpec(spec string) error {
    if len(spec) <= 0 {
        return fmt.Errorf("input spec is empty")
    }

    if spec[0] == '@' {
        return ParseDescriptor(spec)
    }

    fields := strings.Fields(spec)
    if len(fields) != 5 && len(fields) != 6 {
        return errors.New(fmt.Sprintf("Expected 5 or 6 fields, found %d: %s", len(fields), spec))
    }

    return nil
}

/**
 @Description：从cron中移除一个cron job
 @Param:
 @Return：
 */
func RemoveCronJob(cronTask *CrWeaponTask)  {
    CronInstance().RemoveJob(cronTask.Name)
}

/**
 @Description：向cron中增加一个cron job
 @Param:
 @Return：
 */
func AddCronJob(cronTask *CrWeaponTask) error {

    if cronTask.TaskType == CronOtherTask {
        job, exist := ucron.GetJob(cronTask.Target)
        if exist != true {
            return fmt.Errorf("cron task`%s` job model not exist", cronTask.Name)
        }
        // 加入到cron中
        CronInstance().AddJob(cronTask.Spec, job.Job, cronTask.Name)
    } else if cronTask.TaskType == CronWeaponTask {
        // weapon相关的cron task直接加入到cron job中
        var tmp interface{}
        tmp = cronTask
        CronInstance().AddJob(cronTask.Spec, tmp.(cron.Job), cronTask.Name)
    } else {
        return fmt.Errorf("cron task task type `%d` error", cronTask.TaskType)
    }

    return nil
}

//Field name   | Mandatory? | Allowed values  | Allowed special characters
//----------   | ---------- | --------------  | --------------------------
//Seconds      | Yes        | 0-59            | * / , -
//Minutes      | Yes        | 0-59            | * / , -
//Hours        | Yes        | 0-23            | * / , -
//Day of month | Yes        | 1-31            | * / , - ?
//Month        | Yes        | 1-12 or JAN-DEC | * / , -
//Day of week  | Yes        | 0-6 or SUN-SAT  | * / , - ?

/**
 @Description：得到可用的cron的实例对象
 @Param:
 @Return：
 */
func CronInstance() *cron.Cron {
    return cronInstance
}

var cronInstance    *cron.Cron
var cronOnce        sync.Once

/**
 @Description：初始化cron task的接口
 @Param:
 @Return：
 */
func init()  {
    cronOnce.Do(func() {
        cronInstance = cron.New()
        logs.Info("init cron...")
    })

    tmp := CronInstance()
    if tmp == nil {
        logs.Error("init cron instance error.")
    }

    // 启动cron
    tmp.Start()
}